import random
import sys
from RandomVariables import *
from XSY.Base import isnumeric

class Interval(RandomVariable):
   def __init__(self, l=float('-inf') , u = float('inf')):
      '''the closed interval with lower and upper values l and u'''
      if isnumeric(l) and isnumeric(u): 
         self.l, self.u = l, u
      else: raise TypeError("type(l)=%s, type(u)=%s"%type(l),type(u))

   def __hash__(self):
      if self.u == float('inf'):
         return sys.maxint
      elif self.l == float('-inf'):
         return -sys.maxint -1
      else:
         h = self.l+self.u * self.l+1 * self.u+1
         if h == float('inf'): return sys.maxint
         elif h == float('-inf'): return -sys.maxint - 1
         else: return int(h)

   def makeInvalid(self):
      '''make an invalid interval such that l>u'''
      self.l, self.u = float('inf'), 0
      return self

   def valid(self):
      '''check if the interval is valid lie l <= u'''
      return self.l <= self.u

   def invalid(self):
      '''check if the interval is not valid'''
      return not self.valid()

   def makeUniversal(self):
      '''make this = -Infinity ~ Infinity'''
      self.l = float('-inf'); 
      self.u = float('inf') 

   def universal(self):
      '''check if universal (-inf, inf)'''
      self.l == float('-inf') and self.u == float('inf') 

   def makeSingleton(self, v):
      '''make a singleton interval [v, v]'''
      self.l = self.u = v

   def singleton(self):
      '''check if a singleton'''
      self.l == self.u

   def include(self, inv):
      '''check if this interval contains other one'''
      if isinstance(inv, Interval): 
         return self.l <= inv.l and inv.u <= self.u
      else: 
         return False

   def intersect(self, inv):
      '''return the intersection interval between this and inv.
      If there is no intersection, it returns an invalid interval'''
      if(self.invalid() or not isinstance(inv, Interval) or inv.invalid()):
         return Interval().makeInvalid()
      else:
         return Interval(max(self.l, inv.l), min(self.u, inv.u))

   def compare(self, o):
      '''comparer'''
      if isinstance(o, Interval):
         if self.u < o.l:   return -1 # totally less
         elif o.u < self.l: return  1 # totally greater
         elif self.l < o.l: return -1 # partially less
         else:
            if o.l <self.l:    return  1 # partially greater
            elif self.u < o.u: return -1 # partially less
            elif o.u < self.u: return  1 # partially greater
            else:              return  0 # totally equal 
      else: raise Exception(str(o)+" is not an Interval!" )

   def __eq__(self, o): 
      '''equal to o'''
      return self.compare(o) == 0

   def __ne__(self, o):
      '''not equal to o'''
      return self.compare(o) != 0

   def __lt__(self, o):
      '''less than o'''
      return self.compare(o) < 0

   def __le__(self, o):
      '''less than or equal to o'''
      return self.compare(o) <= 0

   def __gt__(self, o):
      '''greater than o '''
      return self.compare(o) > 0

   def __ge__(self,o):
      '''greater than or equal to o'''
      return self.compare(o) >= 0

   def infinity(self):
      '''check if it is infinity [inf,inf]'''
      return self.valid() and self.l == float('inf')

   def __neg__(self):
      '''return -self'''
      return Interval(-self.u, -self.l)

   def __sub__(self,o):
      '''return self - o'''
      return Interval(self.l-o.u, self.u-o.l)

   def __add__(self, o):
      '''return self + o'''
      return Interval(self.l+o.l, self.u+o.u)

   def mergeOver(self, o):
      '''make an interval cover another interval o'''
      if type(o) == type(self):
         self.l = [self.l, o.l].min
         self.u = [self.u, o.u].max
      else: raise Exception(str(type(o)) +" is not "+ str(type(self)))

   def extendBy(self, t):
      '''make the interval cover t'''
      return self.mergeOver(Interval(t,t))

   def __str__(self):
      '''convert it into a string'''
      if self.valid():
         if (self.l == float('-inf')): s = "("+ str(self.l)
         else: s="[" + str(self.l)
         s += "," + str(self.u)
         if (self.u == float('inf')): s += ")" 
         else: s += "]"
      else:
         s = "(%s,%s) is an invalid %s"%(self.l,self.u, self.__class__)
      return s

   def singular(self):
      '''check if the interval has only one value'''
      return self.l == self.u

   def sample(self):
      '''pick one value from the interval'''
      l,u = self.l, self.u
      
      if not self.singular() and l == float('-inf'): l = -sys.float_info.max/10000
      if not self.singular() and u == float('inf'):  u =  sys.float_info.max/10000 

      return random.uniform(l, u)

class TInterval(Interval):
   '''closed interval subset of [0, \infty)'''
   def __init__(self, l=0, u = float('inf')):
      '''constructor'''
      Interval.__init__(self, l,u)

   def valid(self):
      '''check if the interval is valid'''
      return Interval.valid(self) and 0 <= self.l

   def makeUniversal(self):
      '''make this = 0 ~ Infinity.'''
      self.l = 0 
      self.u = float('inf')
       
   def universal(self):
      '''check if universal [0, inf).'''
      return self.l == 0 and self.u == float('inf')
      
   def __str__(self):
      return Interval.__str__(self)

if __name__ == '__main__':
   #it = Interval(float('-inf'),float('inf') )
   #it = Interval(0, 100000) #float('inf') )
   it = Interval(-10000,10000)

   total_s = 0
   n = 10
   for i in range(0, n):
      s = it.sample()
      print "i=%s, %s.sample()=%s"%(i,it,s)
      total_s += s
   print "average=%s"%(total_s/n)


   print Interval(0,2) 
   print Interval(2, 0) 
   print TInterval(-2, 0) 

   a = Interval(-10, 10)
   print "a=%s" % a

   b = TInterval()
   print "b=%s" % b 
   
   print "a+b=%s" % (a+b)
   print "a-b=%s" % (a-b)

